package com.wanxi.gateway.filter;


import com.wanxi.common.result.CommonResult;
import com.wanxi.feign.clients.UserFeignClient;
import com.wanxi.gateway.util.JwtTokenUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Set;

@Order(1)
@Component
public class PermissionFilter implements GlobalFilter {

    @Value("#{'${pathList}'.split(',')}")
    private List<String> pathList;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserFeignClient userFeignClient;


    // 验证权限第一种方法：本地验证
    private Boolean checkLocal(String token, ServerHttpResponse response, String uri) {
        Set<String> auths = null; // 就是用户拥有的 uriList

        try {
            auths = jwtTokenUtil.getAuthsFromToken(token);
        } catch (Exception e) {
            // 处理token过期
            if (e instanceof ExpiredJwtException) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return false;
            }
            e.printStackTrace();
        }
        //验证权限
        boolean b = auths.stream().anyMatch(auth -> StringUtils.equals(auth, uri));
        if (!b) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return false;
        }
        return true;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String uri = request.getURI().getPath();
        //那些路径可以直接放行
        boolean a = pathList.stream().anyMatch(path -> StringUtils.contains(uri, path));
        if (a) {
            return chain.filter(exchange);
        }

        String authorization = request.getHeaders().getFirst("Authorization"); // bearer jwtToken
        if (StringUtils.isBlank(authorization)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();

        }
        String token = StringUtils.substring(authorization, "bearer".length()).trim();

        // todo  此处需要你们去完善这个功能 因为这个校验有个前提  就是所有的uri 都需要存入数据库中
        // todo 应该先判断用户正在访问的uri是否权限表中， 如果不在权限表中,只要token有效,就直接给该用户 放行
        // todo  如果在权限表中  就看当前用户的权限列表 Set<String> uriSet中是否包含正在访问的这个uri
        // 验证权限第一种方法：本地验证(简单的认证)
//        Boolean isAuth = checkLocal(token, response, uri);


//    第二种方法：远程调用验证(复杂的认证)
        boolean isAuth = checkLongRange(response, uri, token);
        if (!isAuth) {
            return response.setComplete();
        }
        return chain.filter(exchange);
    }

    private boolean checkLongRange(ServerHttpResponse response, String uri, String token) {
        try {
            boolean tokenExpired = jwtTokenUtil.isTokenExpired(token);
            if (tokenExpired) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return false;
            }
        } catch (Exception e) {
            // 处理token过期
            if (e instanceof ExpiredJwtException) {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return false;
            }
            e.printStackTrace();
        }
        CommonResult commonResult = userFeignClient.checkAccessToUri(uri, jwtTokenUtil.getUserNameFromToken(token));
        if (commonResult.getCode() == 403) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return false;
        }
        if (commonResult.getCode() == 500) {
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            return false;
        }
        return true;
    }
}